﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Security.Cryptography;
using System.Drawing.Imaging;
using System.IO;

namespace Screener.Imaging
{
    public class Cell
    {
        private byte[] _hash;
        private Size _size;

        public byte[] Hash
        {
            get
            {
                return _hash;
            }
            set
            {
                _hash = value;
            }
        }

        public List<CellInfo> CellInfo { get; set; }

        public Size Size
        {
            get
            {
                return _size;
            }
            set
            {
                _size = value;
            }
        }

        public Byte[] Data { get; set; }

        public Cell(Bitmap bmp, int i, int j, Size size)
        {
            this.CellInfo = new List<CellInfo>();
            this.Size = size;
            this.Data = new byte[this.Size.Width * this.Size.Height];
            int counter = 0;

            unsafe
            {
                BitmapData bData = bmp.LockBits(new Rectangle(i * this.Size.Width, j * this.Size.Height, this.Size.Width, this.Size.Height), ImageLockMode.ReadOnly, bmp.PixelFormat);
                byte* scan0 = (byte*)bData.Scan0.ToPointer();

                for (int x = 0; x < this.Size.Width; x++)
                {
                    for (int y = 0; y < this.Size.Height; y++)
                    {
                        byte* data = scan0 + y * bData.Stride + x * 24 / 8;

                        switch ((x + y) % 3)
                        {
                            case 0:
                                this.Data[counter] = (byte)((data[2] / 64 << 6) | (data[1] / 32 << 3) | (data[0] / 32));
                                break;
                            case 1:
                                this.Data[counter] = (byte)((data[0] / 64 << 6) | (data[2] / 32 << 3) | (data[1] / 32));
                                break;
                            case 2:
                                this.Data[counter] = (byte)((data[1] / 64 << 6) | (data[0] / 32 << 3) | (data[2] / 32));
                                break;
                        }

                        counter++;
                    }
                }

                bmp.UnlockBits(bData);
            }

            this.ComputeHash();
        }

        public Cell()
        {
            this.CellInfo = new List<CellInfo>();
        }

        private void ComputeHash()
        {
            MD5 md5 = MD5.Create();

            this.Hash = md5.ComputeHash(this.Data);
        }

        public void Write(BinaryWriter bw)
        {
            bw.Write((short)this.CellInfo.Count);

            foreach (CellInfo info in this.CellInfo)
            {
                info.Write(bw);
            }

            for (int i = 0; i < this.Data.Length; i++)
            {
                bw.Write(this.Data[i]);
            }
        }

        public void Read(BinaryReader br)
        {
            short ciCount = br.ReadInt16();
            CellInfo info;

            for (int i = 0; i < ciCount; i++)
            {
                info = new CellInfo();
                info.Read(br);

                this.CellInfo.Add(info);
            }

            this.Data = new byte[this.Size.Width * this.Size.Height];

            for (int i = 0; i < this.Data.Length; i++)
            {
                this.Data[i] = br.ReadByte();
            }
        }

        private bool? _IsMonochrome;

        public bool IsMonochrome
        {
            get
            {
                if (this.Data == null)
                    return false;

                if (_IsMonochrome.HasValue)
                {
                    return _IsMonochrome.Value;
                }
                else
                {
                    for (int i = 0; i < this.Data.Length - 1; i++)
                    {
                        if (this.Data[i] != this.Data[i + 1])
                        {
                            _IsMonochrome = false;
                            return false;
                        }
                    }

                    _IsMonochrome = true;
                    return true;
                }

            }
        }

        public int Count
        {
            get
            {
                int count = 0;

                foreach (var item in this.CellInfo)
                {
                    count += item.Indexes.Count;
                }

                return count;
            }
        }

        public static int Compare(Cell a, Cell b)
        {
            if (a.Count < b.Count)
            {
                return 1;
            }
            else if (a.Count > b.Count)
            {
                return -1;
            }

            return 0;
        }

        public void AddCellinfo(short index, int i, int j)
        {
            foreach (CellInfo info in this.CellInfo)
            {
                if (info.X == i && info.Y == j)
                {
                    info.Indexes.Add(index);
                    return;
                }
            }

            this.CellInfo.Add(new CellInfo(index, i, j));
        }
    }
}
